/**
 * 给定N个数组，两类操作：
 * 1 i j x: 将第i个数组的第j个元素变为x
 * 2 i: 求前i个数组的最小值
 * 显然是一个线段树，将N个数组依次连接成一个大数组即可
 */
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;

using llt = long long;
using vi = vector<int>;
using vll = vector<llt>;
using pii = pair<int, int>;
using pll = pair<llt, llt>;

vi A;

struct SegTree{ // 线段树带延迟

using llt = long long;

int N;

using value_type = llt;
vector<value_type> data; // 线段树

using lazy_type = llt;
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    return min(ls, rs);
}

/// 初始化，清零，不用动
void init(int n) {
    data.assign((N = n) + 1 << 2, value_zero());
}

/// 这个函数不用动
void modify(int a, int b, const lazy_type & delta){
    _modify(1, 1, N, a, b, delta);
}

/// 这个函数不用动
value_type query(int a, int b){
    return _query(1, 1, N, a, b);
}

/// 这个函数不用动
void build() {
    _build(1, 1, N);
}

/// 几乎不用动
value_type _query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    int mid = (s + e) >> 1;
    value_type ans = value_zero(); // 如果求最值，这里不能用zero
    if(a <= mid) ans = _up_(ans, _query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, _query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void _modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        data[t] = delta;
        return;
    }
    int mid = (s + e) >> 1;
    if(a <= mid) _modify(lson(t), s, mid, a, b, delta);
    if(mid < b) _modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 几乎不用动
void _build(int t, int s, int e) {
    if(s == e) {
        data[t] = A[s]; // 注意提供value_type的初始化
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}


/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 = 2E9;
    return VALUE0;
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}


};

int N;
SegTree St;
vector<vi> B;
int Total = 0;

vi Index;

void work(){
    cin >> N;
    B.assign(N + 1, {});
    Index.assign(N + 1, 0);
    for(int i=1;i<=N;++i){
        int m; cin >> m;
        B[i].assign(m + 1, 0);
        for(int j=1;j<=m;++j) cin >> B[i][j];
        Total += m;
        Index[i] = Total;
    }

    A.assign(Total + 1, 0);
    int k = 0;
    for(int i=1;i<=N;++i)for(int j=1;j<B[i].size();++j){
        A[++k] = B[i][j];
    }

    St.init(Total);
    St.build();

    int Q; cin >> Q;
    for(int q=1;q<=Q;++q){
        int op;
        cin >> op;
        if(1 == op){
            int i, j, x; cin >> i >> j >> x;
            int p = Index[i - 1] + j;
            St.modify(p, p, x);
        }else if(2 == op){
            int i; cin >> i;
            int p = Index[i];
            auto ans = St.query(1, p);
            cout << ans << "\n";
        }else{
            assert(0);
        }
    }
    return; 
}


int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);	
    int nofkase = 1;
	// cin >> nofkase;
	while(nofkase--) work();
	return 0;
}